package com.example.kuaizu.utils;


import com.example.kuaizu.exception.SendMassageExcepTion;
import com.example.kuaizu.pojo.dto.MassageSendDTO;
import com.example.kuaizu.pojo.ov.PhoneInfoResponse;
import com.fasterxml.jackson.databind.ObjectMapper;
import kuaizu.common.constp.KuaiZuConst;
import kuaizu.common.exception.MassageException;
import lombok.Data;
import okhttp3.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;


/**
 * @Author xiaokai
 * 请求相关工具类
 */
@Component
@Data
public class WxRequestUtils {
    private static final OkHttpClient client = new OkHttpClient.Builder().build();
    private static final Logger log = LoggerFactory.getLogger(WxRequestUtils.class);
    @Value("${wx.secret}")
    private String secret;//小程序 appsecret
    // 使用非静态的 setter 方法注入 appid
    @Value("${wx.appid}")
    private String appid;
    //获取openid
    private final String GET_JSCODE_URL = "https://api.weixin.qq.com/sns/jscode2session";
    //获取手机号
    private final String GET_USER_PHONE_URL = "https://api.weixin.qq.com/wxa/business/getuserphonenumber";

    //获取access_token
    private final String GET_ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token";
    //获取稳定token
    private final String POST_ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/stable_token";
    //发送订阅消息请求
    private final String POST_SEND_MASSAGE_URL = "https://api.weixin.qq.com/cgi-bin/message/subscribe/send";
    @Autowired
    private final RestTemplate restTemplate;
    @Autowired
    private ObjectMapper objectMapper;


    /**
     * 发送 GET 请求
     *
     * @param jsCode 用户登陆code 请求的 URL
     * @return 响应的字符串内容
     * session_key临时登陆凭证 openid用户唯一标识 ,unionid 用户在开放平台的唯一标识符,
     * @throws IOException 网络请求异常
     */
    public String getRequest(String jsCode) throws IOException {
        String url = GET_JSCODE_URL + "?appid=" + appid + "&secret=" + secret + "&js_code=" + jsCode + "&grant_type=authorization_code";
        //log.info("url:{}",url);
        Request request = new Request.Builder().url(url).build();
        try (Response response = client.newCall(request).execute()) {
            if (!response.isSuccessful()) throw new IOException("异常 code " + response);
            return response.body().string();
        }
    }

    /**
     * 发送订阅消息
     *
     * @param res
     * @return
     * @throws IOException
     */
    public String postMassageRequest(MassageSendDTO res) throws IOException {
        String url = POST_SEND_MASSAGE_URL + "?access_token=" + res.getAccessToken();
        //将对象转成json字符串
        String json = objectMapper.writeValueAsString(res);
        log.info("json之后数据:{}", json);
        Request request = new Request.Builder().url(url).post(RequestBody.create(json, MediaType.parse("application/json; charset=utf-8")))
                .build();
        try (Response response = client.newCall(request).execute()) {
            if (!response.isSuccessful()) throw new IOException("异常 code " + response);
            return response.body().string();
        }
    }

    /**
     * 获取access_token
     *
     * @return access_token
     */
    public String getAccessToken() {
        String url = getGET_ACCESS_TOKEN_URL() + "?grant_type=client_credential&" + "appid=" + appid + "&secret=" + secret;
        HashMap map = execute(url, HttpMethod.GET, null, null, new HashMap<String, String>(), HashMap.class);
        log.info("获取的数据:->{}", map);
        //判断请求是否正确
        if (!(null != map && map.get("access_token") != null)) {
            log.info("获取access_token失败");
            String url2 = POST_ACCESS_TOKEN_URL;
            //创建兜底
            Map<String, Object> data = new HashMap<>();
            data.put("grant_type", "client_credential");
            data.put("appid", appid);
            data.put("secret", secret);
            data.put("force_refresh", true);
            log.info("尝试第二次获取请求参数:{}", data);
            map = execute(url2, HttpMethod.POST, data, null, new HashMap<String, String>(), HashMap.class);
            if (!(null != map && map.get("access_token") != null)) {
                throw new MassageException("获取access_token失败");
            }
        }
        return map.get("access_token").toString();
    }

    public PhoneInfoResponse getPhoneNumber(Map<String, String> map) {
        //log.info("传入的jscode:{}",code);
        String url = getGET_USER_PHONE_URL() + "?access_token=" + getAccessToken();
        //Map<String, String> data = new HashMap<>();
        //data.put("code", code);
        //log.info("map:{}", data);
        PhoneInfoResponse req = execute(url, HttpMethod.POST, map, null, new PhoneInfoResponse(), PhoneInfoResponse.class);
        //ResponseEntity<Map> mapResponseEntity = restTemplate.postForEntity(url, data, Map.class);
        log.info("获取到的手机号是{}", req);
        if (KuaiZuConst.WX_REQ_OK == req.getErrcode()) {
            return req;
        }
        throw new MassageException("获取手机号失败调用接口错误码为" + req.getErrcode());
    }


    /**
     * @param url     请求路径
     * @param method  请求方式
     * @param data    请求携带的参数
     * @param type    携带参数的字节码文件,用于转换json
     * @param resData 期待返回值的对象
     * @param resType 期待返回值的独享自己吗文件
     * @param <L>     携带参数相关的泛型
     * @param <V>     期待返回值对象的泛型
     * @return 期待类型的对象
     */
    private <L, V> V execute(String url, HttpMethod method, L data, Class<L> type, V resData, Class<V> resType) {
        String json = restTemplate.execute(url, method, ((ClientHttpRequest req) -> {
            //设置请求头传递的数据格式为json
            req.getHeaders().setContentType(org.springframework.http.MediaType.parseMediaType("application/json; charset=UTF-8"));
            log.info("发请求{} =>请求方式{}  ==>请求参数{}", url, method, data);
            //设置请求体 携带对应的数据 将对应的对象转成json字符串
            // 将对象转换为 JSON 字符串
            //添加请求体
            if (data != null) {
                String jsonBody = objectMapper.writeValueAsString(data);
                //设置请求体
                req.getHeaders().setContentLength(jsonBody.getBytes(StandardCharsets.UTF_8).length);

                OutputStream out = req.getBody();
                out.write(jsonBody.getBytes(StandardCharsets.UTF_8));
                out.flush();
                log.info("转换成的json->{}", jsonBody);
            }
        }), response -> {
            // 处理响应
            log.info("接相应");
            if (response.getStatusCode().is2xxSuccessful()) {
                // 读取响应内容
                byte[] body = response.getBody().readAllBytes();
                log.debug("相应内容{}", new String(body));
                return new String(body);
            } else {
                throw new RuntimeException("请求失败，状态码: " + response.getStatusCode());
            }
        });
        //将json转成对象
        try {

            resData = objectMapper.readValue(json, resType);
        } catch (Exception e) {
            e.printStackTrace();
            //throw new MassageException("序列化失败");
            return null;
        }
        //判断对象是否为null使用hutool包的工具
        if (Objects.isNull(resData)) {
            throw new MassageException("序列化失败");
        }
        //log.info("转换的json独享{}", resData);
        return resData;
    }

    /**
     * 获取发送订阅消息的参数
     */
    public MassageSendDTO getSendParam(String templateId, String openid, String pageUrl, Object dat) {
        //获取token
        String accessToken = getAccessToken();
        MassageSendDTO massageSendDTO = new MassageSendDTO();
        massageSendDTO.setAccessToken(accessToken);
        //模版id
        massageSendDTO.setTemplate_id(templateId);
        massageSendDTO.setTouser(openid);
        massageSendDTO.setPage(pageUrl);
        massageSendDTO.setData(dat);
        //版本
        massageSendDTO.setMiniprogram_state(KuaiZuConst.MINI_PROGRAM_TYPE_TRIAL);
        massageSendDTO.setLang(KuaiZuConst.MINI_PROGRAM_LANGUAGE_ZH_CN);
        return massageSendDTO;
    }

    /**
     * 发送订阅消息
     */
    public void sendMassage(MassageSendDTO dto) {
        try {
            log.info("发送订阅消息参数:{}", dto);
            String s = postMassageRequest(dto);
            log.info("发送订阅消息结果：{}", s);
            //将json转换成map对象
            Map<String, Object> map = objectMapper.readValue(s, Map.class);
            if(!map.get("errcode").equals(0)){
                throw new SendMassageExcepTion(s);
            }
        } catch (IOException e) {
            log.error("发送订阅消息失败:{}", dto);
            throw new MassageException("发送订阅消息失败");
        }
    }
}
